﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using DevExpress.XtraGrid.Views.Grid;
using RedRiver.Framework.XtraCommon.Extensions;
using RedRiver.Framework.XtraCommon.FormHelper;
using RedRiver.Framework.XtraCommon.Models;

namespace RedRiver.Framework.XtraCommon.Templates.Generic{
    public class GridViewSelector<TContext, TModel> : GridViewSelector<TModel>
        where TContext : ObjectContext, new()
        where TModel : EntityObject{
        private readonly Dictionary<object, TModel> _itemDict = new Dictionary<object, TModel>();
        private Func<TModel, object, bool> _findByKey;

        public GridViewSelector(){
            InitDataContext(new TContext());
        }

        public GridViewSelector(TContext context){
            InitDataContext(context);
        }

        protected IEnumerable<TModel> DataSource{
            get { return (IEnumerable<TModel>) defaultBindingSource.DataSource; }
        }

        protected EntityDataContext<TContext> DataContext{
            get;
            set;
        }

        public void AttachEdit<TModel1>(GridView view, Expression<Func<TModel1, object>> expression) where TModel1 : class {
            AttachEdit<TModel1,object>(view, expression);
        }

        public void AttachEdit<TModel1, TValue>(GridView view, Expression<Func<TModel1, TValue>> expression) where TModel1 : class {
            if(view == null){
                throw new ArgumentNullException("view");
            }
            string fieldName = expression.MemberName();
            var column = view.Columns.ColumnByFieldName(fieldName);
            if(column == null){
                view.Columns.AddField(fieldName);
            }
            AttachEdit(column);
        }

        protected void AddDisplayColumnName<TValue>(params Expression<Func<TModel, TValue>>[] expressions){
            foreach (var expression in expressions){
                string memberName = MetadataExtension.MemberName(expression);
                if (!DisplayColumns.Contains(memberName)){
                    DisplayColumns.Add(memberName);
                }
            }
        }

        public void SetKeyColumn<TValue>(Expression<Func<TModel, TValue>> expression){
            KeyColumnName = MetadataExtension.MemberName(expression);
            Func<TModel, TValue> keyCompairFunc = expression.Compile();
            _findByKey = (t, key) => key.Equals(keyCompairFunc(t));
        }

        public override object FindByKey(object keyValue){
            if (keyValue == null){
                return null;
            }
            TModel value;
            if (_itemDict.TryGetValue(keyValue, out value)){
                return value;
            }
            var list = (IEnumerable<TModel>) defaultBindingSource.DataSource;
            TModel result = list.FirstOrDefault(t => _findByKey(t, keyValue));
            if (result != null){
                _itemDict.Add(keyValue, result);
            }
            return result;
        }

        /// <summary>
        /// 初始化 数据上下文默认方法
        /// </summary>
        /// <typeparam name="TContext"></typeparam>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="context"></param>
        protected IEntityDataContext<TContext> InitDataContext(TContext context){
            defaultBindingSource.DataSource = typeof(TModel);
            DataContext = new EntityDataContext<TContext>(context);
            DisplayNameAttribute dispAttr =
                TypeDescriptor.GetAttributes(typeof (TModel)).OfType<DisplayNameAttribute>().FirstOrDefault();
            if (dispAttr != null){
                FormTitle = dispAttr.DisplayName;
            }
            DataContext.OnLoadData =d => DefaultBindingSource.DataSource = d.GetObjectSet<TModel>().ToList();
            
            return DataContext;
        }

        protected override void OnLoadData(){
            base.OnLoadData();
            _itemDict.Clear();
            if (DataContext != null){
                if (InvokeRequired){
                    BeginInvoke(new Action(DataContext.LoadData));
                } else{
                    DataContext.LoadData();
                }
            }
        }
        }
}